using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;


namespace MarbleAddingMachine
{
        
    class Marble
    {
        public enum MarbleState 
        {
            Deletable,
            Input,
            DroppingFirst, 
            SeeSaw, 
            BitCarrying, 
            BitOverflow, 
            DroppingSecond, 
            Output, 
            RollingExit 
        }
        const float exitRampAngle = 185.761088817f;
        public Vector2 exitRampAccel = new Vector2(0.008778202f,0.087007471f);
        const float overFlowRampAngle = 21.25050551f;
        public Vector2 overFlowRampAccel = new Vector2(0.177062917f, 0.455304644f);
        public MarbleState state;
        public Vector2 position;
        public Vector2 Spd;
        Texture2D marble;
        public int no;
        Vector2 step;
        bool rollStage; 

        public Marble(MarbleState startingState, Vector2 startingPos, Texture2D MarbleTex, int bitNo)
        {
            state = startingState;
            position = startingPos;
            marble = MarbleTex;
            no = bitNo;
        }
        public void Update(float[] rotations, ref bool[] chngState, ref bool[] bits, bool outputting)
        {
        
            switch(state)
            {
                case MarbleState.DroppingFirst:
                    #region DroppingFirst
                    if (DroppingFirst(rotations, ref chngState, ref bits))
                    {
                        chngState[no] = true;
                        bits[no] = !bits[no];
                        if (rotations[no] <= 0)
                        {
                            state = MarbleState.SeeSaw;
                            goto case MarbleState.SeeSaw;
                        }
                        else
                        {
                            if (no > 0)
                            {
                                state = MarbleState.BitCarrying;
                                goto case MarbleState.BitCarrying;
                            }
                            else
                            {
                                Spd = Vector2.Zero;
                                step = new Vector2(419,444) - position;
                                step /= 19.85984f;
                                step *= 2;
                                state = MarbleState.BitOverflow;
                            }
                        }
                    }
#endregion
                break;
                case MarbleState.SeeSaw:
                   #region SeeSaw
                   if (Vector2.Distance(position, new Vector2(119 + ((5 - no) * 60), 25 + ((5 - no) * 64))) > 5f)
                {
                    if (!rollStage)
                    {
                        step = new Vector2(119 + ((5 - no) * 60), 25 + ((5 - no) * 64)) - position;
                        step /= 16.25f;
                        step *= 2;
                        rollStage = true;
                    }
                        position += step;
                        step *= 1.5f;
                }
                else
                {
                    rollStage = false;
                    if (!bits[no] && !chngState[no])
                    {
                        rollStage = true;
                        step = new Vector2(143 + ((5 - no) * 60), (int)48f + ((5.0f - no) * 64f)) - position;
                        step /= 16.25f;
                        step *= 2;
                        Spd = Vector2.Zero;
                        state = MarbleState.DroppingSecond;
                        goto case MarbleState.DroppingSecond;
                    }
                }
#endregion
                break;
                case MarbleState.BitCarrying:
                #region BitCarrying
                if (Vector2.Distance(position, new Vector2(70 + ((6 - no) * 60), 80 + ((6 - no) * 64))) > 5f)
                    {
                        if (!rollStage)
                        {
                            step = new Vector2(70 + ((6 - no) * 60), 80 + ((6 - no) * 64)) - position;
                            step /= 19.85984f;
                            step *= 2;
                            rollStage = true;
                        }
                        position += step;
                        step *= 1.2f;
                    }
                    else
                    {
                        rollStage = false;
                        Spd = Vector2.Zero;
                        no -= 1;
                        state = MarbleState.DroppingFirst;
                        goto case MarbleState.DroppingFirst;
                    }
#endregion
                break;
                case MarbleState.DroppingSecond:
                    byte retVal = DroppingSecond(outputting);
                    if (retVal == 1)
                    {
                        goto case MarbleState.Output;
                    }
                    else if (retVal == 2)
                    {
                        Spd = Vector2.Zero;
                        goto case MarbleState.RollingExit;
                    }
                break;
                case MarbleState.Output:
                    if (!outputting)
                    {
                        state = MarbleState.DroppingSecond;
                        goto case MarbleState.DroppingSecond;
                    }
                break;
                case MarbleState.RollingExit:
                if (position.Y < 500)
                {
                    Spd += exitRampAccel;
                    position += Spd;
                }
                else 
                {
                    state = MarbleState.Deletable;
                }
                break;
                case MarbleState.BitOverflow:
                if (rollStage)
                {
                    if (position.Y < 500)
                    {

                        Spd += overFlowRampAccel/1f;
                        position += Spd;
                    }
                    else
                    {
                        state = MarbleState.Deletable;
                    }
                }
                else
                {
                    if (Vector2.Distance(position, new Vector2(419, 444)) > 10)
                    {
                        position += step;
                        step *= 1.5f;
                    }
                    else 
                    {
                        rollStage = true;
                        Spd = overFlowRampAccel*30f;
                        position = new Vector2(419, 444);
                    }
                }
                break;
            }
        }
        public void Draw(SpriteBatch sb)
        {
            sb.Draw(marble, position, null, Color.White, 0, new Vector2(marble.Width / 2, marble.Height / 2), 1f, SpriteEffects.None, 0);
        }
        byte DroppingSecond(bool outputting)
        {
            if (!rollStage)
            {
                if (position.X < 555f)
                {
                    if (no == 5)
                    { 
                    
                    }
                    Spd += new Vector2(3, 0);
                    position += Spd;
                    if (position.X > 555f)
                    {
                        if (outputting)
                        {
                            position.X = 555f;
                            state = MarbleState.Output;
                            Spd = Vector2.Zero;
                            return 1;
                        }
                    }
                }
                else
                {
                    float rampPos = 589f + position.Y * (float)Math.Tan(MathHelper.ToRadians(exitRampAngle));
                    if (no == 5)
                    { 
                    }
                    if (position.X < rampPos)
                    {
                        Spd += new Vector2(3, 0);
                        position += Spd;
                        if (position.X > rampPos)
                        {
                            position.X = rampPos;
                            state = MarbleState.RollingExit;
                            return 2;
                        }
                    }
                    else 
                    {
                        position.X = rampPos;
                        state = MarbleState.RollingExit;
                        return 2;
                    }
                }
            }
            else 
            {

                if (Vector2.Distance(position, new Vector2(143 + ((5 - no) * 60), (int)48f + ((5.0f - no) * 64f))) > 5f)
                {
                    position += step;
                    step *= 1.5f;
                }
                else 
                {
                    rollStage = false;
                    position = new Vector2(143 + ((5 - no) * 60), (int)48f + ((5.0f - no) * 64f));
                }
            }
            return 0;
        }

        bool DroppingFirst(float[] rotations, ref bool[] chngState, ref bool[] bits) 
        {
            
                if (position.X < 155 + (4 - no) * 60)
                {
                    Spd += new Vector2(3, 0);
                    position += Spd;
                    if (position.X > 155 + (4 - no) * 60)
                    {
                        position.X = 155 + (4 - no) * 60;
                    }
                }
                else
                {
                    return true;
                }
            return false;
        }
        public static Vector2 MoveToRotation(float angle, float steps)
        {
            float X = (float)Math.Cos((double)angle) * steps;
            return new Vector2(X, (float)Math.Tan((double)angle) * X);
        }
    }
}
